/*
 * Copyright (C) 2009 Leandro de Oliveira Aparecido <lehphyro@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.japa.interceptor.autopersist;

import java.lang.reflect.*;
import java.util.*;

import org.aopalliance.intercept.*;
import org.aspectj.lang.*;
import org.slf4j.*;
import org.springframework.aop.*;
import org.springframework.aop.aspectj.*;

import com.google.code.japa.annotation.*;
import com.google.code.japa.engine.*;
import com.google.code.japa.interceptor.*;
import com.google.common.base.*;
import com.google.common.collect.*;

/**
 * Intercept calls to methods of classes annotated with {@link AutomaticPersistence}.
 * 
 * @author Leandro Aparecido
 * @since 1.0
 * @see com.google.code.japa.annotation.AutomaticPersistence
 * @see com.google.code.japa.interceptor.JapaAspectSupport
 */
public class AutomaticPersistenceInterceptor extends JapaAspectSupport implements MethodInterceptor {

	private static final Logger logger = LoggerFactory.getLogger(AutomaticPersistenceInterceptor.class);
	
	/**
	 * JapaObjects of the intercepted target objects.
	 * <p>Implemented as a weak identity hashmap.
	 */
	private Map<Object, JapaObject> japaObjects;

	/**
	 * Creates the interceptor with an empty list of JapaObjects.
	 * 
	 * @see com.google.code.japa.engine.JapaObject
	 */
	public AutomaticPersistenceInterceptor() {
		japaObjects = new ReferenceMap<Object, JapaObject>(ReferenceType.WEAK, ReferenceType.STRONG);
	}
	
	/**
	 * Intercepts the method invocation applying the automatic persistence funcionalities.
	 * 
	 * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
	 */
	public Object invoke(MethodInvocation invocation) throws Throwable {
		logger.trace("Intercepting [{}] to process automatic persistence", invocation);
		
		Object target = invocation.getThis();
		Method method = invocation.getMethod();
		Object[] arguments = invocation.getArguments();
		Object returnValue = null;
		Object modifiedReturnValue = null;

		Object[] modifiedArguments = beforeCall(target, method, arguments);
		try {
			ProceedingJoinPoint joinPoint = new MethodInvocationProceedingJoinPoint((ProxyMethodInvocation)invocation);
			returnValue = joinPoint.proceed(modifiedArguments);
		} finally {
			modifiedReturnValue = afterCall(returnValue);
		}
		
		return modifiedReturnValue;
	}

	/**
	 * Retrieves the processed JapaObject of the passed object. Creates one if not found and
	 * stores it in the internal {@link #japaObjects} map.
	 * 
	 * @see com.google.code.japa.interceptor.JapaAspectSupport#getJapaObjectFor(java.lang.Object)
	 */
	@Override
	protected JapaObject getJapaObjectFor(Object obj) {
		JapaObject japaObject = japaObjects.get(obj);
		if (japaObject == null) {
			japaObject = getJapaEngine().processObject(obj);
			japaObjects.put(obj, japaObject);
		}
		
		return japaObject;
	}
	
}
